﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Lucene.Net.Documents;
using NewsMine.Utilities;

namespace LuceneWrap.Lib
{
    public class FeedLuceneReflection
    {
        public static List<Fieldable> GetLuceneFields<T>(T obj, bool isSearch)
        {
            List<Fieldable> fields = new List<Fieldable>();
            List<Fieldable> fieldsFromObjectProperty = null;
            // get all properties of the object type
            PropertyInfo[] propertyInfos = obj.GetType().GetProperties();
            foreach (var propertyInfo in propertyInfos)
            {
                //If property is not null add it as field and it is not a search
                if (obj.GetType().GetProperty(propertyInfo.Name).GetValue(obj, null) != null && !isSearch)
                {
                    if (obj.GetType().GetProperty(propertyInfo.Name).GetValue(obj, null).ToString() == string.Empty)
                        continue;

                    fieldsFromObjectProperty = GetLuceneFieldsForInsertUpdate(obj, propertyInfo, false);
                }
                else
                {
                    if (obj.GetType().GetProperty(propertyInfo.Name).GetValue(obj, null) == null)
                        fieldsFromObjectProperty = null;
                    else
                        fieldsFromObjectProperty = GetLuceneFieldsForInsertUpdate(obj, propertyInfo, true);
                }

                if (fieldsFromObjectProperty != null)
                    fields.AddRange(fieldsFromObjectProperty);
            }

            return fields;
        }

        private static List<Fieldable> GetLuceneFieldsForInsertUpdate<T>(T obj, PropertyInfo propertyInfo, bool isSearch)
        {
            #region New implementation for indexing all the fields that are available in the object
            List<Fieldable> fields = new List<Fieldable>();

            if (propertyInfo.PropertyType == typeof(List<string>))
            {
                List<string> listOfStringsValue = (List<string>)obj.GetType().GetProperty(propertyInfo.Name).GetValue(obj, null);

                foreach (var str in listOfStringsValue)
                {
                    fields.Add(new Field(propertyInfo.Name.ToLower(), TempAnalyzer.RemoveAllSpecialCharacterForIndexing(str), Field.Store.YES, Field.Index.ANALYZED));
                }

                return fields;
            }

            if (propertyInfo.PropertyType == typeof(DateTime))
            {
                //NumericField numericField = new NumericField(propertyInfo.Name, 1, Field.Store.NO, true);
                //numericField.SetLongValue(GetNumberFromDate(obj, propertyInfo));

                //return numericField;

                //if it is not a search assign the object value to the field
                // return new Field(propertyInfo.Name.ToLower(), GetNumberFromDate(obj, propertyInfo).ToString().ToLower(), Field.Store.NO, Field.Index.NOT_ANALYZED);

                //NumericField numericField = new NumericField(propertyInfo.Name, 1, Field.Store.NO, true);
                //numericField.SetLongValue(GetNumberFromDate(obj, propertyInfo));
                //fields.Add(numericField); 

                //                Field f = new Field(propertyInfo.Name.ToLower(), "dt" + GetNumberFromDate(obj, propertyInfo).ToString().ToLower(), Field.Store.NO, Field.Index.NOT_ANALYZED);



                fields.Add(new Field(propertyInfo.Name.ToLower(),
                        DateTools.DateToString(Convert.ToDateTime(obj.GetType().GetProperty(propertyInfo.Name).GetValue(obj, null)), DateTools.Resolution.HOUR),
                        Field.Store.YES, Field.Index.NOT_ANALYZED));



                return fields;


            }

            if (propertyInfo.PropertyType == typeof(int) || propertyInfo.PropertyType == typeof(long))
            {
                long number = Convert.ToInt64(obj.GetType().GetProperty(propertyInfo.Name).GetValue(obj, null));

                //NumericField numericField = new NumericField(propertyInfo.Name, 1, Field.Store.NO, true);
                //numericField.SetLongValue(number);
                //fields.Add(numericField);


                Field f = new Field(propertyInfo.Name.ToLower(), "" + number, Field.Store.YES, Field.Index.UN_TOKENIZED);
                fields.Add(f);

                return fields;
            }

            Field.Store store = Field.Store.NO;
            Lucene.Net.Documents.Field.Index index = Field.Index.ANALYZED;

            if (propertyInfo.Name.ToLower().EndsWith("key") || propertyInfo.Name.ToLower().EndsWith("id"))
            {
                store = Field.Store.YES;
                index = Field.Index.NOT_ANALYZED;
            }

            //if it is not a search assign the object value to the field
            string propertyValue = GetPropertyValue(obj, propertyInfo);


            if (propertyInfo.Name.ToLower().EndsWith("id"))
            {
                NumericField field = new NumericField("n" + propertyInfo.Name.ToLower(), Field.Store.YES, true);

                field.SetLongValue(Convert.ToInt64(propertyValue.ToLower()));

                fields.Add(field);


                Field field1 = new Field(propertyInfo.Name.ToLower(), propertyValue.ToLower(), store, index);

                fields.Add(field1);


                return fields;
            }
            else
            {

                Field field = new Field(propertyInfo.Name.ToLower(), TempAnalyzer.RemoveAllSpecialCharacterForIndexing(propertyValue.ToLower()), store, index);

                fields.Add(field);
                return fields;
            }
            //return field;




            #endregion

            #region Limiting index to the specific attibuted fields

            //Field field = null;


            //object[] dbFieldAtts = propertyInfo.GetCustomAttributes(typeof(LuceneWrapAttribute), isSearch);

            //if (dbFieldAtts.Length > 0 && propertyInfo.PropertyType == typeof(System.String))
            //{
            //    var luceneWrapAttribute = ((LuceneWrapAttribute)dbFieldAtts[0]);
            //    field = GetLuceneField(obj, luceneWrapAttribute, propertyInfo, isSearch);
            //}
            //else if (propertyInfo.PropertyType != typeof(System.String))
            //{
            //    //if the type is not marked for lucene search just ignore it.
            //    //todo: we need to develop the logic to store the numbers as well.
            //    // In future this kind of attribute also needs to be removed; every thing from the object needs to be indexed, apart from marked with not index.

            //    return null;
            //    //throw new InvalidCastException(string.Format("{0} must be a string in order to get indexed", propertyInfo.Name));
            //}


            //return field;

            #endregion

        }

        public static long GetNumberFromDate(object obj, PropertyInfo propertyInfo)
        {
            if (propertyInfo.PropertyType.ToString() == typeof(DateTime).ToString())
            {
                DateTime date = Convert.ToDateTime(obj.GetType().GetProperty(propertyInfo.Name).GetValue(obj, null));

                //return Convert.ToInt64(date.Year.ToString("D4") + date.Month.ToString("D2") + date.Day.ToString("D2") + date.Hour.ToString("D2") + date.Minute.ToString("D2"));
                return Convert.ToInt64(date.Year.ToString("D4") + date.Month.ToString("D2") + date.Day.ToString("D2") + date.Hour.ToString("D2"));
            }
            else
                return 1;
        }

        public static int GetNumberFromInt(object obj, PropertyInfo propertyInfo)
        {
            if (propertyInfo.PropertyType.ToString() == typeof(int).ToString())
            {
                int number = Convert.ToInt32(obj.GetType().GetProperty(propertyInfo.Name).GetValue(obj, null));

                return number;
            }
            else
                return 1;
        }

        public static long GetNumberFromLong(object obj, PropertyInfo propertyInfo)
        {
            if (propertyInfo.PropertyType.ToString() == typeof(int).ToString())
            {
                long number = Convert.ToInt64(obj.GetType().GetProperty(propertyInfo.Name).GetValue(obj, null));

                return number;
            }
            else
                return 1;
        }

        private static string GetPropertyValue(object obj, PropertyInfo propertyInfo)
        {
            if (propertyInfo.PropertyType.ToString() == typeof(string).ToString())
            {
                return obj.GetType().GetProperty(propertyInfo.Name).GetValue(obj, null).ToString();
            }

            //todo: here we need to handle numerics and dates properly.

            return obj.GetType().GetProperty(propertyInfo.Name).GetValue(obj, null).ToString();
        }

        private static Field GetLuceneField<T>(T obj, LuceneWrapAttribute luceneWrapAttribute, PropertyInfo propertyInfo, bool isSearch)
        {
            Field.Store store = luceneWrapAttribute.IsStored ? Field.Store.YES : Field.Store.NO;
            Lucene.Net.Documents.Field.Index index = luceneWrapAttribute.IsSearchable ? Field.Index.ANALYZED : Field.Index.NOT_ANALYZED;
            //if it is not a search assign the object value to the field
            string propertyValue = isSearch ? string.Empty : obj.GetType().GetProperty(propertyInfo.Name).GetValue(obj, null).ToString();
            Field field = new Field(propertyInfo.Name, propertyValue, store, index);
            return field;
        }

        //public static T GetObjFromDocument<T>(Document document) where T : new()
        //{
        //    T obj = new T();

        //    Field[] binaryFields = document.GetFields(CommonConstants.ObjectBinaryFieldName);

        //    if (binaryFields != null && binaryFields.Count() > 0)
        //    {
        //        if (binaryFields.Count() > 1)
        //        {
        //            throw new ApplicationException("We should not have more that one binary object filed. Some where code is wrong.");
        //        }

        //        byte[] buffer = document.GetBinaryValue(CommonConstants.ObjectBinaryFieldName);
        //        obj = (T)ObjectByteArrayConverter.ByteArrayToObject(buffer);
        //        return obj;
        //    }
        //    else
        //    {
        //        throw new ApplicationException("I think we need to make this field as mandotory to save any thing that needs to be persisted.");
        //    }

        //    var fields = GetLuceneFields(obj, true);
        //    foreach (var field in fields)
        //    {


        //        //setting values to properties of the object via reflection
        //        obj.GetType().GetProperty(field.Name()).SetValue(obj, document.Get(field.Name()), null);
        //    }

        //    return (T)obj;
        //}
    }
}
